package u2

import (
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

// FileHandle 封装文件句柄和定时器
type FileHandle struct {
	file  *os.File
	timer *time.Timer
}

var (
	fileMap = sync.Map{} // 存储文件句柄的并发安全map
	mu      sync.Mutex   // 保护文件操作的互斥锁
)

// AppendFile 追加内容到指定文件，自动管理文件句柄
//
// 参数:
//   - filename: 目标文件名
//   - content: 要追加的内容
//
// 返回值:
//   - error: 操作过程中发生的错误
//
// 注意:
//
//	文件句柄会在1秒不活动后自动关闭，如果写入失败会重试3次
func AppendFile(filename, content string) error {
	mu.Lock()
	defer mu.Unlock()

	var handle *FileHandle
	handle2, ok := fileMap.Load(filename)

	if !ok {
		file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
		if err != nil {
			log.Println("open file error:", err)
			return err
		}
		handle = &FileHandle{
			file: file,
			timer: time.AfterFunc(1*time.Second, func() {
				mu.Lock()
				defer mu.Unlock()
				fileMap.Delete(filename)
				file.Close()
			}),
		}
		fileMap.Store(filename, handle)
	} else {
		handle = handle2.(*FileHandle)
	}

	_, err := handle.file.WriteString(content)
	if err != nil {
		if strings.Contains(err.Error(), "write after close") ||
			strings.Contains(err.Error(), "file already closed") {
			return AppendFileWithTimes(filename, content, 3)
		}
		log.Println("write file error:", err)
		return err
	}
	return nil
}

// AppendFileWithTimes 带重试机制的追加文件操作
//
// 参数:
//   - filename: 目标文件名
//   - content: 要追加的内容
//   - times: 最大重试次数
//
// 返回值:
//   - error: 操作过程中发生的错误
func AppendFileWithTimes(filename, content string, times int) error {
	var err error
	for i := 0; i < times; i++ {
		err = AppendFile(filename, content)
		if err == nil {
			break
		}
	}
	return err
}

// CloseAllFiles 关闭所有打开的文件句柄
//
// 注意:
//
//	通常在程序退出时调用，确保所有文件资源被正确释放
func CloseAllFiles() {
	fileMap.Range(func(key, value interface{}) bool {
		filename := key.(string)
		fileMap.Delete(filename)
		handle := value.(*FileHandle)
		handle.timer.Stop()
		handle.file.Close()
		return true
	})
}

// AppendFileOnce 一次性追加内容到文件，不保持文件句柄
//
// 参数:
//   - filename: 目标文件名
//   - content: 要追加的内容
func AppendFileOnce(filename, content string) {
	file, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		log.Println("open file error:", err)
		return
	}
	defer file.Close()
	if _, err := file.WriteString(content); err != nil {
		log.Println("write file error:", err)
	}
}

// ReadFileLine 逐行读取文件内容，调用回调函数处理每一行
//
// 参数:
//   - filename: 要读取的文件名
//   - callback: 处理每一行的回调函数
func ReadFileLine(filename string, callback func(line string)) {
	file, err := os.Open(filename)
	if err != nil {
		log.Println("open file error:", err)
		return
	}
	defer file.Close()

	buf := make([]byte, 1024)
	var line string
	for {
		n, err := file.Read(buf)
		if err != nil {
			if err.Error() == "EOF" {
				if line != "" {
					callback(line)
				}
				break
			}
			log.Println("read file error:", err)
			return
		}

		if n == 0 {
			if line != "" {
				callback(line)
			}
			break
		}

		for i := 0; i < n; i++ {
			if buf[i] == '\n' {
				callback(line)
				line = ""
			} else {
				line += string(buf[i])
			}
		}
	}
}

// ReadFile 读取整个文件内容
//
// 参数:
//   - filename: 要读取的文件名
//
// 返回值:
//   - []byte: 文件内容，如果读取失败返回nil
func ReadFile(filename string) []byte {
	b, err := os.ReadFile(filename)
	if err != nil {
		log.Println("read file error:", err)
		return nil
	}
	return b
}

// ReadFileStringSlice 将文件内容按指定分隔符分割成字符串切片
//
// 参数:
//   - filename: 要读取的文件名
//   - seq: 分隔符，默认为换行符
//
// 返回值:
//   - []string: 分割后的字符串切片
func ReadFileStringSlice(filename string, seq string) []string {
	b := ReadFile(filename)
	if b == nil {
		return make([]string, 0)
	}
	if seq == "" {
		seq = "\n"
	}
	return strings.Split(string(b), seq)
}

// RemoveFile 如果文件存在则删除
//
// 参数:
//   - filename: 要删除的文件名
//
// 返回值:
//   - error: 操作过程中发生的错误
func RemoveFile(filename string) error {
	if _, err := os.Stat(filename); err == nil {
		return os.Remove(filename)
	}
	return nil
}

// ExistFile 检查文件是否存在
//
// 参数:
//   - filename: 要检查的文件名
//
// 返回值:
//   - bool: 文件存在返回true，否则返回false
func ExistFile(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}
